package org.project.single.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.project.single.mapper.SysDictDataMapper;
import org.project.single.mapper.SysDictTypeMapper;
import org.project.single.pojo.constant.CacheNameConst;
import org.project.single.pojo.constant.SysConst;
import org.project.single.pojo.dto.dict.DictDataCacheDto;
import org.project.single.pojo.dto.dict.DictTypeCacheDto;
import org.project.single.pojo.po.SysDictData;
import org.project.single.pojo.po.SysDictType;
import org.project.single.pojo.ro.EnableRo;
import org.project.single.pojo.ro.dict.DictDataListRo;
import org.project.single.pojo.ro.dict.DictTypeListRo;
import org.project.single.pojo.systemEnum.SystemCodeEnum;
import org.project.single.pojo.vo.dict.*;
import org.project.single.service.SysDictService;
import org.project.single.service.SysUserService;
import org.project.single.utils.PoUtil;
import org.redisson.api.BatchResult;
import org.redisson.api.RBatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.stone.project.baseCommon.sysException.BuzException;
import org.stone.project.baseRedis.utils.RedisUtil;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SysDictServiceImpl implements SysDictService {
    @Autowired
    private SysDictTypeMapper sysDictTypeMapper;

    @Autowired
    private SysDictDataMapper sysDictDataMapper;

    @Autowired
    private SysUserService sysUserService;

    @Override
    public Page<DictTypeListVo> getDictTypeListBack(DictTypeListRo dictTypeListRo) {
        Page<DictTypeListVo> res = PageHelper.startPage(dictTypeListRo.getPageNum(), dictTypeListRo.getPageSize())
                .doSelectPage(() -> {
                    sysDictTypeMapper.getDictTypeListBack(dictTypeListRo);
                });

        //信息填充
        List<String> createUserId = res.getResult()
                .stream()
                .map(DictTypeListVo::getUpdateUser)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(createUserId)){
            Map<String, String> userIdNameMap = sysUserService.selectUsernameByIds(createUserId);
            res.getResult().stream()
                    .forEach(ele->{
                        ele.setUpdateUsername(userIdNameMap.get(ele.getUpdateUser()));
                    });
        }
        return res;
    }

    @Override
    public DictTypeDetailVo getDictTypeDetail(String id) {
        SysDictType sysDictType = sysDictTypeMapper.selectById(id);
        if (ObjectUtil.isNotNull(sysDictType)){
            DictTypeDetailVo dictTypeDetailVo = BeanUtil.copyProperties(sysDictType, DictTypeDetailVo.class);
            //查询相关详情
            List<SysDictData> sysDictData = sysDictDataMapper.selectList(Wrappers.lambdaQuery(SysDictData.class).eq(SysDictData::getParentCode, dictTypeDetailVo.getCode()).orderByDesc(SysDictData::getUpdateTime));
            dictTypeDetailVo.setDictDataDetailVoList(sysDictData.stream()
                    .map(ele->BeanUtil.copyProperties(ele,DictDataDetailVo.class))
                    .collect(Collectors.toList()));
            return dictTypeDetailVo;
        }
        throw new BuzException(SystemCodeEnum.DATA_ERR.getCode(),SystemCodeEnum.DATA_ERR.getDescripStr());
    }

    @Override
    @Transactional
    public void editDictType(DictTypeEditVo dictTypeEditVo) {
        SysDictType sysDictType = BeanUtil.copyProperties(dictTypeEditVo, SysDictType.class);
        if (ObjectUtil.isNull(dictTypeEditVo.getId())){
            /**
             * 是否存在相同code
             */
            int has = sysDictTypeMapper.existCode(sysDictType.getCode());
            if (has > 0)
                throw new BuzException(SystemCodeEnum.DATA_ERR_CODE.getCode(),SystemCodeEnum.DATA_ERR_CODE.getDescripStr());

            sysDictType.setId(IdUtil.getSnowflake().nextIdStr());
            sysDictType.setEnableFlag(0);
            PoUtil.initInsert(sysDictType,new Date());
            sysDictTypeMapper.insert(sysDictType);
        }else{
            PoUtil.initUpdate(sysDictType,new Date());
            sysDictTypeMapper.updateById(sysDictType);
        }

        //缓存刷新
        updateDictTypeCache(1,sysDictType.getCode(),sysDictTypeMapper.selectById(sysDictType.getId()));
    }

    @Override
    @Transactional
    public void deleteDictType(String id) {
        SysDictType data = sysDictTypeMapper.selectById(id);
        if (ObjectUtil.isNotNull(data)){
            sysDictTypeMapper.deleteById(id);
            sysDictDataMapper.delete(Wrappers.lambdaQuery(SysDictData.class).eq(SysDictData::getParentCode,data.getCode()));

            //缓存刷新
            updateDictTypeCache(2,data.getCode(),data);
        }
    }

    @Override
    @Transactional
    public void enableDictType(EnableRo enableRo) {
        SysDictType sysDictType = sysDictTypeMapper.selectById(enableRo.getId());
        if (ObjectUtil.isNotNull(sysDictType)){
            SysDictType sysDictTypeUpdate = BeanUtil.copyProperties(enableRo, SysDictType.class);
            int updateNum = sysDictTypeMapper.updateById(sysDictTypeUpdate);

            //子类刷新
            int sonUpdateNum = sysDictDataMapper.update(null, Wrappers.lambdaUpdate(SysDictData.class).eq(SysDictData::getParentCode, sysDictType.getCode())
                    .set(SysDictData::getEnableFlag, enableRo.getEnableFlag())
            );


            //缓存刷新
            if (updateNum>0){
                updateDictTypeCache(1,sysDictType.getCode(),sysDictType);
            }
            String sonRedisKey = CacheNameConst.getCacheName(CacheNameConst.DICT_DATA_CACHE_DATA, sysDictType.getCode());
            if (sonUpdateNum>0){
                Map<String, DictDataCacheDto> sonMap = sysDictDataMapper.selectList(Wrappers.lambdaQuery(SysDictData.class).eq(SysDictData::getParentCode, sysDictType.getCode()))
                        .stream()
                        .map(ele -> {
                            return BeanUtil.copyProperties(ele, DictDataCacheDto.class);
                        })
                        .collect(Collectors.toMap(DictDataCacheDto::getKeyCode, ele -> ele));
                RBatch batch = RedisUtil.getRedissonClient().createBatch();
                batch.getKeys().deleteAsync(sonRedisKey);
                batch.getMap(sonRedisKey).putAllAsync(sonMap);
                batch.execute();
            }else {
                RedisUtil.keyDelete(sonRedisKey);
            }
        }
    }

    @Override
    public Page<DictDataListVo> getDictDataListBack(DictDataListRo dictDataListRo) {
        Page<DictDataListVo> res = PageHelper.startPage(dictDataListRo.getPageNum(), dictDataListRo.getPageSize())
                .doSelectPage(() -> {
                    sysDictDataMapper.getDictDataListBack(dictDataListRo);
                });

        //信息填充
        List<String> createUserId = res.getResult()
                .stream()
                .map(DictDataListVo::getUpdateUser)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(createUserId)){
            Map<String, String> userIdNameMap = sysUserService.selectUsernameByIds(createUserId);
            res.getResult().stream()
                    .forEach(ele->{
                        ele.setUpdateUsername(userIdNameMap.get(ele.getUpdateUser()));
                    });
        }
        return res;
    }

    @Override
    public DictDataDetailVo getDictDataDetail(String id) {
        SysDictData sysDictData = sysDictDataMapper.selectById(id);
        if (ObjectUtil.isNotNull(sysDictData)){
            return BeanUtil.copyProperties(sysDictData, DictDataDetailVo.class);
        }
        throw new BuzException(SystemCodeEnum.DATA_ERR.getCode(),SystemCodeEnum.DATA_ERR.getDescripStr());
    }

    @Override
    @Transactional
    public void editDictData(DictDataEditVo dictDataEditVo) {
        SysDictData sysDictData = BeanUtil.copyProperties(dictDataEditVo, SysDictData.class);
        if (ObjectUtil.isNull(dictDataEditVo.getId())){
            /**
             * 是否存在相同code
             */
            int has = sysDictDataMapper.existCode(sysDictData.getParentCode(),sysDictData.getKeyCode());
            if (has > 0)
                throw new BuzException(SystemCodeEnum.DATA_ERR_CODE.getCode(),SystemCodeEnum.DATA_ERR_CODE.getDescripStr());

            sysDictData.setId(IdUtil.getSnowflake().nextIdStr());
            sysDictData.setSortNum(SysConst.sortNum);
            sysDictData.setEnableFlag(0);
            PoUtil.initInsert(sysDictData,new Date());
            sysDictDataMapper.insert(sysDictData);
        }else{
            PoUtil.initUpdate(sysDictData,new Date());
            sysDictDataMapper.updateById(sysDictData);
        }

        //缓存刷新
        updateDictDataCache(1,sysDictData.getParentCode(),sysDictDataMapper.selectById(sysDictData.getId()));
    }

    @Override
    @Transactional
    public void deleteDictData(String id) {
        SysDictData sysDictData = sysDictDataMapper.selectById(id);
        if (ObjectUtil.isNotNull(sysDictData)){
            sysDictDataMapper.deleteById(id);

            //缓存刷新
            updateDictDataCache(2,sysDictData.getParentCode(),sysDictData);
        }
    }

    @Override
    @Transactional
    public void enableDictData(EnableRo enableRo) {
        SysDictData dt = sysDictDataMapper.selectById(enableRo.getId());
        if (ObjectUtil.isNotNull(enableRo)){
            SysDictType sysDictType = sysDictTypeMapper.selectOne(Wrappers.lambdaQuery(SysDictType.class).eq(SysDictType::getCode, dt.getParentCode()));
            if (ObjectUtil.isNotNull(sysDictType)){
                if (sysDictType.getEnableFlag() == 0 && enableRo.getEnableFlag() == 1)
                    throw new BuzException(SystemCodeEnum.DATA_ERR.getCode(),"请先开启"+sysDictType.getCode()+"字典");

                SysDictData sysDictData = BeanUtil.copyProperties(enableRo, SysDictData.class);
                int num = sysDictDataMapper.updateById(sysDictData);
                if (num > 0){
                    dt.setEnableFlag(sysDictData.getEnableFlag());
                    updateDictDataCache(1,dt.getParentCode(),dt);
                }
            }
        }
    }

    @Override
    public List<SysDictData> findDictDataCacheDtoDbByCode(String code) {
        return sysDictDataMapper.selectList(Wrappers.lambdaQuery(SysDictData.class).eq(SysDictData::getParentCode,code).orderByAsc(SysDictData::getSortNum).orderByDesc(SysDictData::getUpdateTime));
    }

    @Override
    public SysDictType findDictTypeCacheDtoDbByCode(String code) {
        return sysDictTypeMapper.selectOne(Wrappers.lambdaQuery(SysDictType.class).eq(SysDictType::getCode, code));
    }

    /**
     * dictType数据缓存刷新
     * @param opt 1编辑   2删除
     * @param sysDictType
     */
    public void updateDictTypeCache(Integer opt,String code,SysDictType sysDictType) {
        String cacheKey = CacheNameConst.DICT_TYPE_CACHE_DATA+code;
        DictTypeCacheDto dictTypeCacheDto = BeanUtil.copyProperties(sysDictType, DictTypeCacheDto.class);
        if (opt == 1){
            RedisUtil.getRBucket(cacheKey).set(dictTypeCacheDto);
        }else{
            RedisUtil.keyDelete(cacheKey);
        }
    }

    /**
     * dictData数据缓存刷新
     * @param opt 1编辑   2删除
     * @param sysDictData
     */
    public void updateDictDataCache(Integer opt,String code,SysDictData sysDictData){
        String cacheKey = CacheNameConst.DICT_DATA_CACHE_DATA+code;
        DictDataCacheDto dictDataCacheDto = BeanUtil.copyProperties(sysDictData, DictDataCacheDto.class);
        if (opt == 1){
            RedisUtil.getRMap(cacheKey).put(sysDictData.getKeyCode(),dictDataCacheDto);
        }else {
            RedisUtil.getRMap(cacheKey).remove(sysDictData.getKeyCode());
        }
    }

    /**
     * dictData数据缓存全量刷新
     * @param code
     * @param dictDataList
     */
    public void updateDictDataAllCache(String code, Collection<SysDictData> dictDataList){
        String cacheKey = CacheNameConst.DICT_DATA_CACHE_DATA+code;
        Map<String, DictDataCacheDto> dataMap = dictDataList.stream()
                .map(ele -> {
                    return BeanUtil.copyProperties(ele, DictDataCacheDto.class);
                })
                .collect(Collectors.toMap(DictDataCacheDto::getKeyCode,ele->ele));
        RBatch batch = RedisUtil.getRedissonClient().createBatch();
        batch.getKeys().deleteAsync(cacheKey);
        batch.getMap(cacheKey).putAllAsync(dataMap);
        batch.execute();
    }
}
